package cn.net.isir.service2;

import cn.net.isir.common.*;
import cn.net.isir.common.config.WxConfig;
import cn.net.isir.core.BasicService2;
import cn.net.isir.dao.*;
import cn.net.isir.entity.*;
import cn.net.isir.utils.HttpUtils;
import cn.net.isir.utils.StringUtils;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author liuxubo
 * Filename WxminipPayService.java
 * Package cn.net.isir.entity
 * Email lxb@isir.net.cn
 * Date 2018/09/18
 * Comment
 * @since 1.7
 */
@Service("minipWxminipPayService")
public class WxminipPayService extends BasicService2<WxMinipPayUnifiedOrderInfo> {

    @Resource
    private WxMinipPayUnifiedOrderInfoDao wxMinipPayUnifiedOrderInfoDao;
    @Resource
    private WxMinipPayUnifiedOrderResponseInfoDao wxMinipPayUnifiedOrderResponseInfoDao;
    @Resource
    private OrderDao orderDao;
    @Resource
    private ShopDao shopDao;
    @Resource
    private PaymentDao paymentDao;


    /**
     * 统一下单接口
     *
     * @param clientIP 客户端IP
     * @param unionid  unionid
     * @param orderid  订单id
     * @param shopid   店铺id
     * @return 下单结果
     */
    public ResultObject uniformOrder(String clientIP, String unionid, String shopid, String orderid) {
        ResultObject ro = new ResultObject();
        //查询openid信息
        WxMinipUserinfo userInfo = CacheManager.getWxUserInfo(unionid);
        String openid = userInfo.getOpenid();
        //查询订单信息
        Order order = orderDao.selectByPrimaryKey(orderid);
        if (order == null) {
            ro.setResult(500);
            ro.setMessage("订单不存在");
            return ro;
        }
        if (order.getIsPaid() == 1) {
            ro.setResult(500);
            ro.setMessage("该订单已支付");
            return ro;
        }
        if (!shopid.equals(order.getShopId())) {
            ro.setResult(500);
            ro.setMessage("店铺和订单信息不匹配");
            return ro;
        }
        //查询店铺信息
        Shop shop = shopDao.selectByPrimaryKey(order.getShopId());
        //如果未绑定支付信息，则无法调起支付
        if (shop.getWxpayMerchantId() == null) {
            ro.setResult(501);
            ro.setMessage("商户尚未绑定支付信息，无法在线支付。");
            return ro;
        }

        String sub_mch_id = null;
        Payment payment = paymentDao.selectByPrimaryKey(shop.getWxpayMerchantId());

        //组装实体类
        WxMinipPayUnifiedOrderInfo uniformForm = new WxMinipPayUnifiedOrderInfo();
        uniformForm.setAppid(WxConfig.getAppid());// 微信分配的公众账号ID
        uniformForm.setMch_id(WxConfig.getMch_id());// 微信支付分配的商户号
        uniformForm.setSub_appid(WxConfig.getSub_appid());// 当前调起支付的小程序APPID
        uniformForm.setSub_mch_id(payment.getSignAccount());// 微信支付分配的子商户号
//        uniformForm.setDevice_info();// 自定义参数，可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"
        uniformForm.setNonce_str(StringUtils.randomUUIDUpperCase());// 随机字符串，长度要求在32位以内。推荐随机数生成算法
//        uniformForm.setSign();// 通过签名算法计算得出的签名值，详见签名生成算法
        uniformForm.setSign_type("MD5");// 签名类型，默认为MD5，支持HMAC-SHA256和MD5。
        uniformForm.setBody(shop.getName() + "-点餐");// 商品简单描述，该字段请按照规范传递，具体请见参数规定
//        uniformForm.setDetail();// 商品详细描述，对于使用单品优惠的商户，改字段必须按照规范上传，详见“单品优惠参数说明”
//        uniformForm.setAttach();// 附加数据，在查询API和支付通知中原样返回，可作为自定义参数使用。
        uniformForm.setOut_trade_no(order.getNo());// 商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见商户订单号
//        uniformForm.setFee_type();// 符合ISO 4217标准的三位字母代码，默认人民币：CNY，详细列表请参见货币类型
        String total_fee_Str = String.valueOf(order.getFinalTotal().multiply(new BigDecimal(100)));
        //去掉最后的小数点和两个0
        total_fee_Str = total_fee_Str.substring(0, total_fee_Str.length() - 3);
        uniformForm.setTotal_fee(total_fee_Str);// 订单总金额，单位为分，详见支付金额
        uniformForm.setSpbill_create_ip(clientIP);// APP和网页支付提交用户端ip，Native支付填调用微信支付API的机器IP。
//        uniformForm.setTime_start();// 订单生成时间，格式为yyyyMMddHHmmss，如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
//        uniformForm.setTime_expire();// 订单失效时间，格式为yyyyMMddHHmmss，如2009年12月27日9点10分10秒表示为20091227091010。订单失效时间是针对订单号而言的，由于在请求支付的时候有一个必传参数prepay_id只有两小时的有效期，所以在重入时间超过2小时的时候需要重新请求下单接口获取新的prepay_id。其他详见时间规则。建议：最短失效时间间隔大于1分钟
//        uniformForm.setGoods_tag();// 订单优惠标记，使用代金券或立减优惠功能时需要的参数，说明详见代金券或立减优惠
        uniformForm.setNotify_url(WxConfig.getNotify_url());// 异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数。
        uniformForm.setTrade_type("JSAPI");// 小程序取值如下：JSAPI，详细说明见参数规定
//        uniformForm.setProduct_id();// trade_type=NATIVE时（即扫码支付），此参数必传。此参数为二维码中包含的商品ID，商户自行定义。
//        uniformForm.setLimit_pay();// 上传此参数no_credit--可限制用户不能使用信用卡支付
//        uniformForm.setOpenid(openid);// trade_type=JSAPI时，此参数必传，用户在商户appid下的唯一标识。openid如何获取，可参考【获取openid】。
        //填写sub_openid后,就不能填写openid，否则会报appid and openid not match错误
        uniformForm.setSub_openid(openid);// trade_type=JSAPI，此参数必传，用户在子商户appid下的唯一标识。openid和sub_openid可以选传其中之一，如果选择传sub_openid,则必须传sub_appid。下单前需要调用【网页授权获取用户信息】接口获取到用户的Openid。
//        uniformForm.setRemark();// 文档地址：https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1&index=1
        uniformForm.setStatus("paying");// 订单状态
        uniformForm.setCreateUser(unionid);// 创建者
//        uniformForm.setCreateTime();// 创建时间
//        uniformForm.setUpdateTime();// 更新时间
//        uniformForm.setIsValid(1);// 是否可用
        logger.info(uniformForm.toString());

        //组织xml数据
        XML rootElement = XML.generateXML("xml");
        rootElement.addChild("appid", uniformForm.getAppid());// 服务商ID	appid	是	String(32)	wxd678efh567hg6787	微信分配的公众账号ID
        rootElement.addChild("mch_id", uniformForm.getMch_id());// 商户号	mch_id	是	String(32)	1230000109	微信支付分配的商户号
        rootElement.addChild("sub_appid", uniformForm.getSub_appid());// 小程序的APPID	sub_appid	是	String(32)	wx8888888888888888	当前调起支付的小程序APPID
        rootElement.addChild("sub_mch_id", uniformForm.getSub_mch_id());// 子商户号	sub_mch_id	是	String(32)	1900000109	微信支付分配的子商户号
        rootElement.addChild("device_info", uniformForm.getDevice_info());// 设备号	device_info	否	String(32)	013467007045764	终端设备号(门店号或收银设备ID)，注意：PC网页或公众号内支付请传"WEB"
        rootElement.addChild("nonce_str", uniformForm.getNonce_str());// 随机字符串	nonce_str	是	String(32)	5K8264ILTKCH16CQ2502SI8ZNMTM67VS	随机字符串，不长于32位。推荐随机数生成算法
//        rootElement.addChild("sign", uniformForm.getSign());// 签名	sign	是	String(32)	C380BEC2BFD727A4B6845133519F3AD6	签名，详见签名生成算法
        rootElement.addChild("sign_type", uniformForm.getSign_type());// 签名类型	sign_type	否	String(32)	HMAC-SHA256	签名类型，目前支持HMAC-SHA256和MD5，默认为MD5
        rootElement.addChild("body", uniformForm.getBody());// 商品描述	body	是	String(128)	腾讯充值中心-QQ会员充值   商品简单描述，该字段须严格按照规范传递，具体请见参数规定
        rootElement.addChild("detail", uniformForm.getDetail());// 商品详情	detail	否	String(6000)	 	商品详细描述，对于使用单品优惠的商户，改字段必须按照规范上传，详见“单品优惠参数说明”
        rootElement.addChild("attach", uniformForm.getAttach());// 附加数据	attach	否	String(127)	说明	附加数据，在查询API和支付通知中原样返回，该字段主要用于商户携带订单的自定义数据
        rootElement.addChild("out_trade_no", uniformForm.getOut_trade_no());// 商户订单号	out_trade_no	是	String(32)	1217752501201407033233368018	商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见商户订单号
        rootElement.addChild("fee_type", uniformForm.getFee_type());// 货币类型	fee_type	否	String(16)	CNY	符合ISO 4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型
        rootElement.addChild("total_fee", uniformForm.getTotal_fee());// 总金额	total_fee	是	Int	888	订单总金额，只能为整数，详见支付金额
        rootElement.addChild("spbill_create_ip", uniformForm.getSpbill_create_ip());// 终端IP	spbill_create_ip	是	String(16)	123.12.12.123	APP和网页支付提交用户端ip，Native支付填调用微信支付API的机器IP。
        rootElement.addChild("time_start", uniformForm.getTime_start());// 交易起始时间	time_start	否	String(14)	20091225091010	订单生成时间，格式为yyyyMMddHHmmss，如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
        rootElement.addChild("time_expire", uniformForm.getTime_expire());// 交易结束时间	time_expire	否	String(14)	20091227091010  订单失效时间，格式为yyyyMMddHHmmss，如2009年12月27日9点10分10秒表示为20091227091010。订单失效时间是针对订单号而言的，由于在请求支付的时候有一个必传参数prepay_id只有两小时的有效期，所以在重入时间超过2小时的时候需要重新请求下单接口获取新的prepay_id。其他详见时间规则
        rootElement.addChild("goods_tag", uniformForm.getGoods_tag());// 订单优惠标记	goods_tag	否	String(32)	WXG	订单优惠标记，代金券或立减优惠功能的参数，说明详见代金券或立减优惠
        rootElement.addChild("notify_url", uniformForm.getNotify_url());// 通知地址	notify_url	是	String(256)	http://www.weixin.qq.com/wxpay/pay.php	接收微信支付异步通知回调地址，通知url必须为直接可访问的url，不能携带参数。
        rootElement.addChild("trade_type", uniformForm.getTrade_type());// 交易类型	trade_type	是	String(16)	JSAPI	小程序取值如下：JSAPI，详细说明见参数规定
        rootElement.addChild("limit_pay", uniformForm.getLimit_pay());// 指定支付方式	limit_pay	否	String(32)	no_credit	no_credit--指定不能使用信用卡支付
        rootElement.addChild("openid", uniformForm.getOpenid());// 用户标识	openid	否	String(128)	oUpF8uMuAJO_M2pxb1Q9zNjWeS6o	trade_type=JSAPI，此参数必传，用户在商户appid下的唯一标识。openid如何获取，可参考【获取openid】。
        rootElement.addChild("sub_openid", uniformForm.getSub_openid());// 用户子标识	sub_openid	否	String(128)	oUpF8uMuAJO_M2pxb1Q9zNjWeS6o	trade_type=JSAPI，此参数必传，用户在子商户appid下的唯一标识。openid和sub_openid可以选传其中之一，如果选择传sub_openid,则必须传sub_appid。下单前需要调用【网页授权获取用户信息】接口获取到用户的Openid。
        String sign = rootElement.sign();
        rootElement.addChild("sign", sign);
        String xmlStr = rootElement.toString();

        //将实体类保存到数据库
        wxMinipPayUnifiedOrderInfoDao.insert(uniformForm);
        logger.info("=========================微信支付统一订单请求XML==============================");
        logger.info(xmlStr);
        logger.info("=========================微信支付统一订单请求XML==============================");
        //请求腾讯支付地址
        String unifiedOrder = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        String resultStr = HttpUtils.sendXMLByPOST(unifiedOrder, xmlStr, "utf-8");
        logger.info("=========================微信支付统一订单返回结果==============================");
        logger.info(resultStr);
        logger.info("=========================微信支付统一订单返回结果==============================");
        //解析返回结果
        Document document;
        try {
            document = DocumentHelper.parseText(resultStr);
        } catch (DocumentException e) {
            e.printStackTrace();
            ro.setResult(500);
            ro.setMessage("下单失败!");
            return ro;
        }
        Element root = document.getRootElement();
        List elementList = root.elements();//root.attributes();
        logger.info(elementList);
        // 组织腾讯支付接口返回的实体
        WxMinipPayUnifiedOrderResponseInfo responseInfo = new WxMinipPayUnifiedOrderResponseInfo();
        responseInfo.setOrderInfoId(uniformForm.getId());
        for (Object elementObj : elementList) {
            Element element = (Element) elementObj;
            if ("return_code".equals(element.getName())) {
                responseInfo.setReturn_code(String.valueOf(element.getData()));
            }
            if ("return_msg".equals(element.getName())) {
                responseInfo.setReturn_msg(String.valueOf(element.getData()));
            }
            if ("appid".equals(element.getName())) {
                responseInfo.setAppid(String.valueOf(element.getData()));
            }
            if ("mch_id".equals(element.getName())) {
                responseInfo.setMch_id(String.valueOf(element.getData()));
            }
            if ("sub_appid".equals(element.getName())) {
                responseInfo.setSub_appid(String.valueOf(element.getData()));
            }
            if ("sub_mch_id".equals(element.getName())) {
                responseInfo.setSub_mch_id(String.valueOf(element.getData()));
            }
            if ("device_info".equals(element.getName())) {
                responseInfo.setDevice_info(String.valueOf(element.getData()));
            }
            if ("nonce_str".equals(element.getName())) {
                responseInfo.setNonce_str(String.valueOf(element.getData()));
            }
            if ("sign".equals(element.getName())) {
                responseInfo.setSign(String.valueOf(element.getData()));
            }
            if ("result_code".equals(element.getName())) {
                responseInfo.setResult_code(String.valueOf(element.getData()));
            }
            if ("err_code".equals(element.getName())) {
                responseInfo.setErr_code(String.valueOf(element.getData()));
            }
            if ("err_code_des".equals(element.getName())) {
                responseInfo.setErr_code_des(String.valueOf(element.getData()));
            }
            if ("trade_type".equals(element.getName())) {
                responseInfo.setTrade_type(String.valueOf(element.getData()));
            }
            if ("prepay_id".equals(element.getName())) {
                responseInfo.setPrepay_id(String.valueOf(element.getData()));
            }
            if ("code_url".equals(element.getName())) {
                responseInfo.setCode_url(String.valueOf(element.getData()));
            }
        }
        responseInfo.setCreateUser(unionid);
        String timestamp = String.valueOf(System.currentTimeMillis()).substring(0, 10);
        responseInfo.setRemark(timestamp);
        wxMinipPayUnifiedOrderResponseInfoDao.insert(responseInfo);
        //如果请求状态和业务状态都成功，返回给小程序请求信息，让小程序使用信息请求支付
        if ("SUCCESS".equals(responseInfo.getReturn_code()) && "SUCCESS".equals(responseInfo.getResult_code())) {
            ro.setResult(200);
            ro.setMessage("success");
            Map<String, Object> map = new HashMap<>();
            map.put("appId", WxConfig.getSub_appid());
            map.put("timeStamp", timestamp);
            map.put("nonceStr", uniformForm.getNonce_str());
            map.put("package", "prepay_id=" + responseInfo.getPrepay_id());
            map.put("signType", "MD5");
            XML xml = XML.generateXML("xml");
            for (String key : map.keySet()) {
                xml.addChild(key, String.valueOf(map.get(key)));
            }
            map.put("paySign", xml.sign());
            logger.info(map);
            //更新订单支付状态
            order.setStatus("paying");
            orderDao.update(order);
            ro.setObject(map);
            return ro;
        }
        if (responseInfo.getReturn_code() != null) {
            ro.setResult(500);
            ro.setMessage(WxErrCode.wxPayErr(responseInfo.getResult_code()));
        }
        ro.setResult(500);
        ro.setMessage(responseInfo.getReturn_msg());
        return ro;
    }

    public static void main(String[] args) {
        logger.info(String.valueOf(System.currentTimeMillis()).substring(0, 10));
        Map<String, Object> map = new HashMap<>();
        map.put("appId", WxConfig.getSub_appid());
        map.put("timeStamp", String.valueOf(System.currentTimeMillis()).substring(0, 10));
        map.put("nonceStr", "ffffff");
        map.put("package", "prepay_id=" + "46666");
        map.put("signType", "MD5");
        XML xml = XML.generateXML("xml");
        for (String key : map.keySet()) {
            xml.addChild(key, String.valueOf(map.get(key)));
        }
        map.put("paySign", xml.sign());
        Logger.getLogger(WxminipPayService.class).info(map);
//        logger.info(new WxminipPayService().uniformOrder("8.8.8.8", "15641a65sdf", "123"));
    }

}
